package menus.common;

import java.awt.Color;
import java.awt.geom.Point2D;
import java.util.ArrayList;


import components.intertitles.TrialInstructions;

import fr.lri.swingstates.animations.Animation;
import fr.lri.swingstates.canvas.CEllipse;
import fr.lri.swingstates.canvas.CShape;
import fr.lri.swingstates.canvas.CStateMachine;
import fr.lri.swingstates.canvas.Canvas;
import fr.lri.swingstates.gestures.Gesture;

/**
 * Skeleton for marking menus
 * 
 * @author mathieu
 *
 */
public abstract class AbstractMenu {
	
	/**
	 * The canvas where are drawn the menu items
	 */
	protected Canvas canvas;
	
	/**
	 * The menu items
	 */
	protected AbstractItem[][] items;
	
	/**
	 * The state machine controlling the menu behavior
	 */
	protected CStateMachine stateMachine;
	
	/**
	 * The location of the menu
	 */
	protected Point2D location;
	
	/**
	 * The gesture analyzer used by this menu
	 */
	protected AbstractGestureAnalyzer analyzer;
	
	/**
	 * Start time of the global selection gesture.
	 * Used in the experiment.
	 */
	protected long startTime;

	/**
	 * Duration of the global selection gesture.
	 * Used in the experiment.
	 */
	protected long selectionDuration;
	
	/**
	 * Orientation error of the selection gesture. Depends on the menu type.
	 * Used in the experiment.
	 */
	protected double orientationError;
	
	/**
	 * Shape error of the selection gesture. Depends on the menu type.
	 * Used in the experiment.
	 */
	protected boolean shapeError;
	
	/**
	 * Length error of the selection gesture. Depends on the menu type.
	 * Used in the experiment.
	 */
	protected double lengthError;
	
	/**
	 * Position error of the selection gesture. Depends on the menu type.
	 * Used in the experiment.
	 */
	protected double positionError;
	
	/**
	 * Target item index.
	 * Used in the experiment.
	 */
	protected int targetIndex;
	
	/**
	 * List of the animations linked to this state machine. Used to stop them at the same time.
	 */
	protected ArrayList<Animation> animations;
	
	/**
	 * Indicates if novice mode is allowed
	 */
	protected boolean noviceAllowed = true;
	
	
	
	// ----------------------------------------------------------------- XXX

	
	
	/**
	 * Constructor
	 * @param canvas
	 * @param itemLabels
	 */
	public AbstractMenu(Canvas canvas, String[][] itemLabels) {
		
		this.canvas = canvas;
		
		init(canvas, itemLabels);
		
		items = new AbstractItem[itemLabels.length][];
		for (int i = itemLabels.length-1; i > -1; i--){
			items[i] = new AbstractItem[itemLabels[i].length];
			for (int j = 0; j < itemLabels[i].length; j++){
				items[i][j] = createItem(itemLabels, i, j, this.toString());
			}
		}
		
		location = new Point2D.Double(0, 0);
		
		animations = new ArrayList<Animation>();
		
	}
	
	
	/**
	 * @return The menu canvas
	 */
	public Canvas getCanvas(){
		return canvas;
	}
	
	/**
	 * @return The menu state machine
	 */
	public CStateMachine getStateMachine(){
		return stateMachine;
	}
	
	/**
	 * @param sm The new menu state machine
	 */
	public void setStateMachine(CStateMachine sm){
		this.stateMachine = sm;
	}
	
	/**
	 * Translates every item of the menu by the specified coordinates
	 * @param dx
	 * @param dy
	 */
	public void translateBy(double dx, double dy){
		
		for (int i = 0; i < items.length; i++){
			for (int j = 0; j < items[i].length; j++){
				if (items[i][j] != null)
					items[i][j].translateBy(dx, dy);
			}
		}
		
		location.setLocation(location.getX() + dx, location.getY() + dy);
	}
	
	/**
	 * Translates every item of the menu to the specified point
	 * @param where
	 */
	public void translateTo(Point2D where){
		
		double dx = where.getX() - location.getX();
		double dy = where.getY() - location.getY();
		
		translateBy(dx, dy);
		
	}
	
	/**
	 * Called in the constructor, before the creation of the items. 
	 * Does nothing. Can be rewritten in a subclass. 
	 * @param canvas
	 * @param itemLabels
	 */
	protected void init(Canvas canvas, String[][] itemLabels){ }
	
	public AbstractGestureAnalyzer getAnalyzer(){
		return analyzer;
	}
	
	/**
	 * @return The amount of item "levels". The meaning of "level" depends on the menu.
	 */
	public int getItemsLength(){
		return items.length;
	}
	
	/**
	 * @param i
	 * @return The amount of items in the ith menu "level". The meaning of "level" depends on the menu.
	 */
	public int getItemsLength(int i){
		return items[i].length;
	}
	
	/**
	 * @return The timestamp of the selection beginning
	 */
	public long getStartTime() {
		return startTime;
	}

	/**
	 * @return The duration of the selection
	 */
	public long getSelectionDuration() {
		return selectionDuration;
	}

	/**
	 * @return The angular difference between the required selection gesture and
	 * the user gesture
	 */
	public double getOrientationError() {
		return orientationError;
	}

	/**
	 * @return true if there has been a shape error during the selection, false either
	 */
	public boolean isShapeError() {
		return shapeError;
	}

	/**
	 * @return The length difference between the required selection gesture and the
	 * user gesture
	 */
	public double getLengthError() {
		return lengthError;
	}
	
	/**
	 * @return The angular difference between the required selection gesture and the
	 * user gesture (zone only)
	 */
	public double getPositionError() {
		return positionError;
	}
	
	public boolean isError(){
		return (getOrientationError() != 0 || getLengthError() != 0 || isShapeError() || getPositionError() != 0);
	}
	
	public void setExperimentMeasures(){
		
		setStartTime();
		setSelectionDuration();
		setOrientationError();
		setShapeError();
		setLengthError();
		setPositionError();
	}
	
	public int getTargetIndex(){
		return targetIndex;
	}
	
	public void setTargetIndex(int ti){
		this.targetIndex = ti;
	}
	
	
	
	
	
	/**
	 * Draws a miniature of the ideal gesture needed for the current target item in the specified canvas
	 * Needs to be extended, depending on the menu type
	 */
	public void drawInstructions(Canvas canvas, CShape zone, int centerRadius, String... tags){
		
		CEllipse center = (CEllipse)canvas.newEllipse(zone.getCenterX() - centerRadius/2, zone.getCenterY() - centerRadius/2, centerRadius, centerRadius)
						.setOutlined(false)
						.setFilled(true)
						.setFillPaint(Color.blue)
						
						.addTag(TrialInstructions.InstructionTag)
						
						.setPickable(true)
						.setDrawable(true);
		
		for (String tag : tags){
			center.addTag(tag);
		}
		
	}

	public void drawInstructions(Canvas c, CShape zone, int centerRadius){
		drawInstructions(c, zone, centerRadius, this.toString());
	}
	
	public void drawInstructions(Canvas canvas, CShape zone, String... tags){
		drawInstructions(canvas, zone, 10, tags);
	}
	
	public void drawInstructions(Canvas c, CShape zone){
		drawInstructions(c, zone, this.toString());
	}
	
	public void addAnimation(Animation a){
		animations.add(a);
	}
	
	public void stopAllAnimations(){
		
		for (Animation a : animations){
			a.stop();
		}
		animations.clear();
	}
	
	public static void drawMenu(){}
	
	public void enableNoviceMode(){
		noviceAllowed = true;
	}
	
	public void disableNoviceMode(){
		noviceAllowed = false;
	}
	
	public boolean isNoviceAllowed(){
		return noviceAllowed;
	}
	
	
	// ----------------------------------------------------------------- XXX
	//							Abstract stuff
	// ----------------------------------------------------------------- XXX

	
	
	/**
	 * @param gesture
	 * @return The item corresponding to this gesture. The interpretation depends on the menu.
	 */
	public abstract AbstractItem getItem(Gesture gesture);
	
	/**
	 * @param itemLabels
	 * @param i
	 * @param j
	 * @return A menu item corresponding to this menu
	 */
	protected abstract AbstractItem createItem(String[][] itemLabels, int i, int j, String tag);
	
	protected abstract void setOrientationError();
	protected abstract void setShapeError();
	protected abstract void setLengthError();
	protected abstract void setPositionError();
	
	public abstract void resetSelection();
	// stateMachine.resetSelection();

	/**
	 * Used in the experiment.
	 * @return true if the user ended a selection, false either
	 */
	public abstract boolean isSelectionEnded();
		// return stateMachine.isSelectionEnded();
	
	
	protected abstract void setSelectionDuration();
		// selectionDuration = stateMachine.getSelectionTimes()[1];

	protected abstract void setStartTime();
		// startTime = stateMachine.getSelectionTimes()[0];
	
	public abstract void drawAllInstructions(Canvas canvas, CShape zone, String... tags);
	
	
	
}
